package main

import (
	"database/sql"
	"fmt"
	"time"

	"gitee.com/yscsky/yu"
	"gitee.com/yscsky/yu/dbopt"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

type Demo struct {
	UID        string    `gorm:"type:varchar(32);primaryKey;not null"`
	Title      string    `gorm:"type:text;not null"`
	Content    string    `gorm:"type:text;not null"`
	ReadCount  int       `gorm:"type:int;not null"`
	CreateTime yu.TStamp `gorm:"type:bigint;not null"`
	UpdateTime yu.TStamp `gorm:"type:bigint;not null"`
}

var db dbopt.IGormOpt

func init() {
	db = dbopt.MustOpenMySQL(dbopt.DSN{
		Username: "root",
		Password: "root",
		URL:      "127.0.0.1",
		Port:     "3306",
		DBName:   "demo",
		SkipTran: false,
		PreStmt:  true,
		LogLevel: 4,
	})
}

func main() {
	defer db.Close()
	if err := db.CreateTable(&Demo{}); err != nil {
		yu.LogErr(err, "create table")
		return
	}
	uid := yu.UUID()
	insert(uid)
	d, err := selectOne(uid)
	if err != nil {
		return
	}
	selectList()
	selectOneDemo(uid)
	d.ReadCount = 100
	d.UpdateTime = yu.NewStampTime(time.Now())
	if err = updateOne(d); err == nil {
		d, _ = selectOne(d.UID)
		yu.Logf("update one: %v", d)
	}
	count()
	queryAll()
	insertList()
}

func insert(uid string) {
	d := &Demo{
		UID:        uid,
		Title:      "title",
		Content:    "content",
		ReadCount:  0,
		CreateTime: yu.NewStampTime(time.Now()),
		UpdateTime: yu.NewStampTime(time.Now()),
	}
	yu.LogErr(db.Insert(d), "Insert")
	yu.LogErr(db.InsertTb("demos", d, clause.OnConflict{DoNothing: true}), "InsertTb")
}

func selectOne(uid string) (d *Demo, err error) {
	d = &Demo{}
	if err = db.Query(&d, "select * from demos where uid=?", uid); err != nil {
		yu.LogErr(err, "select")
		return
	}
	yu.Logf("select one: %v", d)
	return
}

func selectList() {
	list := make([]*Demo, 0)
	if err := db.Query(&list, "select * from demos where create_time<?", time.Now().Unix()); err != nil {
		yu.LogErr(err, "select")
		return
	}
	for _, d := range list {
		yu.Logf("select list: %v", d)
	}
}

func selectOneDemo(uid string) {
	q := &Demo{UID: uid}
	d := &Demo{}
	if err := db.Query(&d, "select * from demos where uid=@uid", sql.Named("uid", q.UID)); err != nil {
		yu.LogErr(err, "select")
		return
	}
	yu.Logf("select one demo: %v", d)
}

func updateOne(d *Demo) error {
	const stmt = "update demos set read_count=?,update_time=? where uid=?"
	if _, err := db.ExecSQL(stmt, d.ReadCount, d.UpdateTime, d.UID); err != nil {
		yu.LogErr(err, "update")
		return err
	}
	return nil
}

func count() {
	count := 0
	if err := db.Query(&count, "select count(uid) from demos"); err != nil {
		yu.LogErr(err, "count")
		return
	}
	yu.Logf("count: %d", count)
}

func queryAll() {
	if err := db.QueryRows(func(g *gorm.DB, r *sql.Rows) error {
		d := &Demo{}
		if err := g.ScanRows(r, &d); err != nil {
			return err
		}
		yu.Logf("uid: %s", d.UID)
		return nil
	}, "select * from demos"); err != nil {
		yu.LogErr(err, "QueryRows")
	}
}

func insertList() {
	list := make([]*Demo, 300)
	for i := 0; i < 300; i++ {
		list[i] = &Demo{
			UID:        yu.UUID(),
			Title:      fmt.Sprintf("title-%d", i),
			Content:    fmt.Sprintf("content-%d", i),
			CreateTime: yu.NewStampTime(time.Now()),
			UpdateTime: yu.NewStampTime(time.Now()),
		}
	}
	forInsert(list[:150])
	batchInsert(list[150:])
}

func forInsert(list []*Demo) {
	defer yu.Cost("Insert")()
	yu.Logf("gorm list len: %d", len(list))
	for i := 0; i < len(list); i++ {
		db.Insert(list[i])
	}
}

func batchInsert(list []*Demo) {
	defer yu.Cost("BatchInsert")()
	yu.Logf("gorm list len: %d", len(list))
	db.BatchInsert(list, 0)
}
